#include "workqueuewr.h"

Workqueuewr::Workqueuewr() : SchedulingHeuristic() {
	this->name = "WQR";
}

Workqueuewr::~Workqueuewr() {
}

long double Workqueuewr::schedule(Grid *grid, Job *job) {

	fprintf(stderr,"\nScheduling with Workqueue With Replication!\n");
	this->job = job;
	this->grid = grid;
	
	if (this->adaptivePartioner!=NULL) {
		this->adaptivePartioner->start_process();
	}

	///
	while (job->hasTaskNotFinished()) {

		/*	Allocation Phase */
		while (job->hasTaskNotScheduled()) {
			
			for (int t=0; t <job->getNumberOfTasks(); t++) {
				if  (job->getTaskByIndex(t)->notScheduled()) {
					
					for (int s = 0; s< grid->getNumberOfSites(); s++) {
						if  (!grid->getSiteByIndex(s)->allProcessorsAreBusy())  {
							int taskChoosenIndex = t;
							int siteChoosenIndex = s;
							int procChoosenIndex = this->getFirstAvailableProcessorAt(s);
							
							Task *task = job->getTaskByIndex(taskChoosenIndex);
							fprintf(stdout,"\nTask '%s' has been scheduled to '%s' at processor '%s' at time %Lf", task->getName(), grid->getSiteByIndex(siteChoosenIndex)->getDomain(), grid->getSiteByIndex(siteChoosenIndex)->getProcessorByIndex(procChoosenIndex)->getName(), SG_getClock());
							
							this->assign(taskChoosenIndex, siteChoosenIndex, procChoosenIndex);					
							break;
						}
						
					}
	
				}
			}
	
			while (grid->isTotallyBusy()) {
				this->doSimGridSimulation(SG_ANY_TASK);
			}

		}
		////
		
		int taskChoosenIndex = -1;
		int siteChoosenIndex = -1;
		int procChoosenIndex = -1;
		/*Minimum replication degree*/
		long int replicationDegreeMin = job->getMinReplicationDegree();
		
		
		
		if ((this->adaptivePartioner!=NULL) && (!this->adaptivePartioner->workload_completed())) {
			//this->adaptivePartioner->add_task(-1);
			for (int t=0; t <job->getNumberOfTasks(); t++) {
				if  (job->getTaskByIndex(t)->notScheduled()) {
					
					for (int s = 0; s< grid->getNumberOfSites(); s++) {
						if  (!grid->getSiteByIndex(s)->allProcessorsAreBusy())  {
							int taskChoosenIndex = t;
							int siteChoosenIndex = s;
							int procChoosenIndex = this->getFirstAvailableProcessorAt(s);
							
							Task *task = job->getTaskByIndex(taskChoosenIndex);
							fprintf(stdout,"\nTask '%s' has been scheduled to '%s' at processor '%s' at time %Lf", task->getName(),grid->getSiteByIndex(siteChoosenIndex)->getDomain(), grid->getSiteByIndex(siteChoosenIndex)->getProcessorByIndex(procChoosenIndex)->getName(), SG_getClock());
							
							this->assign(taskChoosenIndex, siteChoosenIndex, procChoosenIndex);					
							break;
						}
						
					}
	
				}
			}
		} else if(! job->hasTaskNotScheduled() ) {

			/*	Replication Phase	 */
			/*Choose the task that must be replicated*/
			for (int t=0; t < job->getNumberOfTasks(); t++) {
				Task *task = job->getTaskByIndex(t);
				if ((task->isRunning()) && (task->getNumberOfReplicas() == replicationDegreeMin) && (task->getNumberOfReplicas() <= 2) ) {
					
					for (int s = 0; s < grid->getNumberOfSites(); s++) {
						if  (!grid->getSiteByIndex(s)->allProcessorsAreBusy()) {
							taskChoosenIndex = t;
							siteChoosenIndex = s;
							procChoosenIndex = this->getFirstAvailableProcessorAt(s);
							
							//Create and assign replica
							//Task *task = job->getTaskByIndex(taskChoosenIndex);
							task->replicate(task->getDataInput()->getSize());
							
							fprintf(stdout,"\nReplica '%s_%d' has been scheduled to '%s' at processor '%s' at time %Lf", task->getName(), (task->getNumberOfReplicas()), grid->getSiteByIndex(siteChoosenIndex)->getDomain(), grid->getSiteByIndex(siteChoosenIndex)->getProcessorByIndex(procChoosenIndex)->getName(), SG_getClock());
							
							assignReplica(taskChoosenIndex, (task->getNumberOfReplicas() - 1), siteChoosenIndex, procChoosenIndex);
							break;
	
						}
					}
					
				}
			}
		
		}

		while ((grid->isTotallyBusy()) || (taskChoosenIndex==-1)) {
			this->doSimGridSimulation(SG_ANY_TASK);
			taskChoosenIndex=-2;
		}
		
		////
		
	}
	////

	
	int maxReplicationDegree = -1;
	for (int t = 0 ; t < job->getNumberOfTasks(); t++) {
		Task *task = job->getTaskByIndex(t);
		if (task->getNumberOfReplicas()> maxReplicationDegree) {
			maxReplicationDegree = task->getNumberOfReplicas();
		}
	}
	
	fprintf(stdout, "\n----Maximum Replication Degree: %d\n", maxReplicationDegree);
	fprintf(stdout, "\n----Resource Wasting Percentage: %Lg\n", this->resourceWastingPercentage());	
	
	/*Makespan for this execution*/
	long double makespan = SG_getClock();

	return makespan;
	

}

void Workqueuewr::doSimGridSimulation(SG_Task task_to_watch) {
	
	SG_Task *tasks_finished = SG_simulate(-1, task_to_watch, SG_VERBOSE);
	
	//Executed only during the replication phase
	this->killAllReplicasOf(tasks_finished);
	
	this->taskFinished(tasks_finished);

	/*Get task state distribution*/
 	SG_getStateDistribution(&numberOfTasksNotScheduled, &numberOfTasksScheduled, &numberOfTasksReady, 
 	&numberOfTasksRunning, &numberOfTasksFailed, &numberOfTasksFinished);
	 	
 	fprintf(stderr,"\n------------Distribution of tasks-------------");
 	fprintf(stderr,"\nNot Scheduled=%d", this->numberOfTasksNotScheduled);
 	fprintf(stderr,"\nScheduled=%d", this->numberOfTasksScheduled);	
 	fprintf(stderr,"\nReady=%d", this->numberOfTasksReady);
 	fprintf(stderr,"\nRunning=%d", this->numberOfTasksRunning);
 	fprintf(stderr,"\nFailed=%d", this->numberOfTasksFailed);
 	fprintf(stderr,"\nFinished=%d", this->numberOfTasksFinished);
 	fprintf(stderr,"\n----------------------------------------------\n");
 	
}
